<?php

declare(strict_types=1);

namespace Frontend;

class Router
{
    private array $routes = [];

    public function get(string $path, callable $handler): void
    {
        $this->addRoute('GET', $path, $handler);
    }

    public function post(string $path, callable $handler): void
    {
        $this->addRoute('POST', $path, $handler);
    }

    private function addRoute(string $method, string $path, callable $handler): void
    {
        $this->routes[] = [
            'method' => $method,
            'path' => $path,
            'handler' => $handler,
            'pattern' => $this->compilePath($path),
        ];
    }

    public function dispatch(string $uri, string $method): void
    {
        $path = parse_url($uri, PHP_URL_PATH) ?? '/';
        foreach ($this->routes as $route) {
            if ($route['method'] !== $method) {
                continue;
            }
            if (preg_match($route['pattern'], $path, $matches)) {
                array_shift($matches);
                $params = $this->extractParamNames($route['path'], $matches);
                $handler = $route['handler'];
                $reflection = new \ReflectionFunction(\Closure::fromCallable($handler));
                if ($reflection->getNumberOfParameters() > 0) {
                    $handler($params);
                } else {
                    $handler();
                }
                return;
            }
        }

        http_response_code(404);
        require __DIR__ . '/Views/pages/404.php';
    }

    private function compilePath(string $path): string
    {
        $pattern = preg_replace('#\{[^/]+\}#', '([^/]+)', $path);
        return '#^' . $pattern . '$#';
    }

    private function extractParamNames(string $path, array $matches): array
    {
        $segments = explode('/', trim($path, '/'));
        $params = [];
        $index = 0;
        foreach ($segments as $segment) {
            if (preg_match('#^\{([^}]+)\}$#', $segment, $paramMatch)) {
                $params[$paramMatch[1]] = $matches[$index] ?? null;
                $index++;
            }
        }
        return $params;
    }
}

